//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "pprocess.h"
#include "pobject.h"
#include "pcentral.h"
#include "pobjenum.h"
#include "pseudo.h"
#include <aura.h>

#define Lock(hCritical)     do { \
                                AuraCriticalSection_Enter(hCritical); \
                            } while(0)

#define Unlock(hCritical)   do { \
                                AuraCriticalSection_Leave(hCritical); \
                            } while(0)

#define REMOTE_PROCESS_UNSTARTED()       (AURA_INVALID_PID == m_processId)
#define REMOTE_PROCESS_ALREADY_EXITED()  (m_bFinished)
#define REMOTE_PROCESS_JUST_EXITED(ec)   (E_PROCESS_ALREADY_EXITED == ec)

CPseudoProcess::~CPseudoProcess()
{
    if (NULL != m_hLock) {
        AuraCriticalSection_Destroy(m_hLock);
        m_hLock = NULL;
    }
}

ECode CPseudoProcess::Initialize()
{
    ECode ec = NOERROR;

    ec = AuraCriticalSection_Create(&m_hLock);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    if (AURA_INVALID_PID != m_processId) {
        WStringBuf_<32> name;

        name.Copy(ELAURA_PROCESS_PREFIX_NAME);
        name.Append((Int32)m_processId);
        ec = CPseudoSyscall::FindRunningObject(name, (PInterface *)&m_pProxy);
        if (FAILED(ec)) {
            goto ErrorExit;
        }
    }

    return ec;

ErrorExit:
    if (NULL != m_hLock) {
        AuraCriticalSection_Destroy(m_hLock);
        m_hLock = NULL;
    }

    return ec;
}

PInterface CPseudoProcess::Probe(
    /* [in]  */ REIID riid)
{
    if (riid == EIID_IInterface) {
        return (PInterface)(IProcess *)this;
    }
    else if (riid == EIID_IProcess) {
        return (IProcess *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (PInterface)&ECLSID_CProcess;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (PInterface)&EIID_IProcess;
    }

    return NULL;
}

UInt32 CPseudoProcess::AddRef(void)
{
    Int32 nRef = m_refs.Increment();
    return (UInt32)nRef;
}

UInt32 CPseudoProcess::Release(void)
{
    Int32 nRef;

    nRef = m_refs.Decrement();
    if (0 == nRef) {
        PseudoObject::S_Delete(PseudoObjectType_Process, this);
        Lock(m_hLock);
        if (NULL != m_pProxy) {
            m_pProxy->Release();
            m_pProxy = NULL;
        }
        CPseudoCentral::Process_Release(m_processId);
        Unlock(m_hLock);
        delete this;
    }
    return (UInt32)nRef;
}

ECode CPseudoProcess::Aggregate(
    /* [in] */ AggregateType type,
    /* [in] */ PInterface pObject)
{
    return E_NOT_IMPLEMENTED;
}

ECode CPseudoProcess::GetDomain(
    /* [out] */ PInterface *ppObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CPseudoProcess::Start(
    /* [in] */ WString wsName,
    /* [in] */ WString wsArgs)
{
    ECode ec = NOERROR;

    if (wsName.IsNullOrEmpty()
        || (wsName.GetLength(MAXIMUM_FULL_PATH_LENGTH) < 0)) {
        return E_INVALID_ARGUMENT;
    }
    if (!wsArgs.IsNull()
        && (wsArgs.GetLength(MAXIMUM_ARGS_LENGTH) < 0)) {
        return E_INVALID_ARGUMENT;
    }

    Lock(m_hLock);
    if (!REMOTE_PROCESS_UNSTARTED()) {
        Unlock(m_hLock);
        return E_PROCESS_ALREADY_STARTED;
    }

    ec = CPseudoCentral::Process_Create(m_priorityRank, m_capacity,
                                      wsName, wsArgs,
                                      &m_processId);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    Unlock(m_hLock);

    PseudoObject::S_Attach(PseudoObjectType_Process, this);
    return NOERROR;

ErrorExit:
    Unlock(m_hLock);

    return ec;
}

ECode CPseudoProcess::Kill()
{
    ECode ec = NOERROR;

    if (REMOTE_PROCESS_UNSTARTED()) {
        return E_INVALID_OPERATION;
    }

    ec = CPseudoCentral::Process_Kill(m_processId, 3);
    if (FAILED(ec)) {
        return ec;
    }

    Lock(m_hLock);
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
    }
    Unlock(m_hLock);

    return ec;
}


ECode CPseudoProcess::WaitForExit(
    /* [in]  */ Millisecond32 timeout,
    /* [out] */ WaitResult *pResult)
{
    WaitResult wr;
    ECode ec = NOERROR;

    if (NULL == pResult) {
        pResult = &wr;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        return E_PROCESS_NOT_STARTED;
    }

    ec = CPseudoCentral::Process_WaitForExit(m_processId, timeout, pResult);
    if (SUCCEEDED(ec) && (WaitResult_OK == *pResult)) {
        Lock(m_hLock);
        m_bFinished = TRUE;
        if (NULL != m_pProxy) {
            m_pProxy->Release();
            m_pProxy = NULL;
        }
        Unlock(m_hLock);
    }
    return ec;
}

ECode CPseudoProcess::IsAlive(
    /* [out] */ Boolean *pIsAlive)
{
    ECode ec = NOERROR;

    if (NULL == pIsAlive) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        *pIsAlive = FALSE;
        return ec;
    }

    Lock(m_hLock);
    if (m_bFinished) {
        *pIsAlive = FALSE;
        Unlock(m_hLock);
        return ec;
    }
    ec = CPseudoCentral::Process_IsAlive(m_processId, pIsAlive);
    if (SUCCEEDED(ec) && (!*pIsAlive)) {
        m_bFinished = TRUE;
        if (NULL != m_pProxy) {
            m_pProxy->Release();
            m_pProxy = NULL;
        }
    }
    Unlock(m_hLock);

    return ec;
}

ECode CPseudoProcess::_GetProxy() // only called under Lock condition.
{
    if (NULL != m_pProxy) {
        assert(!m_bFinished);
        return NOERROR;
    }

    WStringBuf_<32> srvName;
    WaitResult wr;
    ECode ec = NOERROR;

    srvName.Copy(ELAURA_PROCESS_PREFIX_NAME);
    srvName.Append((Int32)m_processId);

    ec = CPseudoSyscall::WaitForRunningObject(srvName, 200, &wr);

    if (FAILED(ec)) {
        goto ErrorExit;
    }
    ec = CPseudoSyscall::FindRunningObject(srvName, (PInterface *)&m_pProxy);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    return NOERROR;
ErrorExit:
    m_bFinished = TRUE;
    return E_PROCESS_ALREADY_EXITED;
}

ECode CPseudoProcess::SetPriorityRank(
    /* [in] */ ThreadPriorityRank priorityRank)
{
    ECode ec = NOERROR;

    switch (priorityRank) {
        case ThreadPriorityRank_Normal:
        case ThreadPriorityRank_SoftRealTime:
            break;
        case ThreadPriorityRank_HardRealTime:
            return E_NOT_IMPLEMENTED;
        default:
            return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        m_priorityRank = priorityRank;
        return ec;
    }

    Lock(m_hLock);
    if (REMOTE_PROCESS_ALREADY_EXITED()
        || FAILED(_GetProxy())) {
        goto RemoteProcessExited;
    }
    ec = m_pProxy->SetPriorityRank(priorityRank);
    if (REMOTE_PROCESS_JUST_EXITED(ec)) {
        goto RemoteProcessExited;
    }
    Unlock(m_hLock);
    return ec;

RemoteProcessExited:
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
        m_bFinished = TRUE;
    }
    Unlock(m_hLock);
    return E_PROCESS_ALREADY_EXITED;
}

ECode CPseudoProcess::GetPriorityRank(
    /* [out] */ ThreadPriorityRank * pPriorityRank)
{
    ECode ec = NOERROR;

    if (NULL == pPriorityRank) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        *pPriorityRank = m_priorityRank;
        return ec;
    }

    Lock(m_hLock);
    if (REMOTE_PROCESS_ALREADY_EXITED()
        || FAILED(_GetProxy())) {
        goto RemoteProcessExited;
    }

    ec = m_pProxy->GetPriorityRank(pPriorityRank);
    if (REMOTE_PROCESS_JUST_EXITED(ec)) {
        goto RemoteProcessExited;
    }
    Unlock(m_hLock);
    return ec;

RemoteProcessExited:
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
        m_bFinished = TRUE;
    }
    Unlock(m_hLock);
    return E_PROCESS_ALREADY_EXITED;
}

ECode CPseudoProcess::SetThreadPoolCapacity(
    /* [in] */ Int32 capacity)
{
    ECode ec = NOERROR;

    if (REMOTE_PROCESS_UNSTARTED()) {
        m_capacity = capacity;
        return ec;
    }

    Lock(m_hLock);
    if (REMOTE_PROCESS_ALREADY_EXITED()
        || FAILED(_GetProxy())) {
        goto RemoteProcessExited;
    }

    ec = m_pProxy->SetThreadPoolCapacity(capacity);
    if (REMOTE_PROCESS_JUST_EXITED(ec)) {
        goto RemoteProcessExited;
    }
    Unlock(m_hLock);
    return ec;

RemoteProcessExited:
    if (NULL == m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
        m_bFinished = TRUE;
    }
    Unlock(m_hLock);
    return E_PROCESS_ALREADY_EXITED;
}

ECode CPseudoProcess::GetThreadPoolState(
    /* [out] */ Int32 * pCapacity,
    /* [out] */ Int32 * pThreads)
{
    ECode ec = NOERROR;

    if ((NULL == pCapacity) && (NULL == pThreads)) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        if (NULL != pCapacity) {
            *pCapacity = m_capacity;
        }
        if (NULL != pThreads) {
            *pThreads = 0;
        }
        return ec;
    }

    Lock(m_hLock);
    if (REMOTE_PROCESS_ALREADY_EXITED()
        || FAILED(_GetProxy())) {
        goto RemoteProcessExited;
    }

    ec = m_pProxy->GetThreadPoolState(pCapacity, pThreads);
    if (REMOTE_PROCESS_JUST_EXITED(ec)) {
        goto RemoteProcessExited;
    }
    Unlock(m_hLock);
    return ec;

RemoteProcessExited:
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
        m_bFinished = TRUE;
    }
    Unlock(m_hLock);
    return E_PROCESS_ALREADY_EXITED;
}

ECode CPseudoProcess::GetAllThreads(
    /* [out] */ IObjectEnumerator ** ppThreads)
{
    ECode ec = NOERROR;

    if (NULL == ppThreads) {
        return E_INVALID_ARGUMENT;
    }

    CPseudoEnumeratorBuilder builder;
    if (REMOTE_PROCESS_UNSTARTED()) {
        goto RemoteProcessUnstarted;
    }

    Lock(m_hLock);
    if (REMOTE_PROCESS_ALREADY_EXITED()
        || FAILED(_GetProxy())) {
        goto RemoteProcessExited;
    }
    ec = builder.BuildForThread(m_processId, m_pProxy, ppThreads);
    if (FAILED(ec)) {
        goto RemoteProcessExited;
    }
    Unlock(m_hLock);

    return ec;

RemoteProcessExited:
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
        m_bFinished = TRUE;
    }
    Unlock(m_hLock);
RemoteProcessUnstarted:
    return builder.BuildForDummy(ppThreads);
}

ECode CPseudoProcess::GetAllModules(
    /* [out] */ IObjectEnumerator ** ppModules)
{
    ECode ec = NOERROR;

    if (NULL == ppModules) {
        return E_INVALID_ARGUMENT;
    }

    CPseudoEnumeratorBuilder builder;
    if (REMOTE_PROCESS_UNSTARTED()) {
        goto RemoteProcessUnstarted;
    }

    Lock(m_hLock);
    if (REMOTE_PROCESS_ALREADY_EXITED()
        || FAILED(_GetProxy())) {
        goto RemoteProcessExited;
    }
    ec = builder.BuildForModule(m_processId, m_pProxy, ppModules);
    if (FAILED(ec)) {
        goto RemoteProcessExited;
    }
    Unlock(m_hLock);

    return ec;

RemoteProcessExited:
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
        m_bFinished = TRUE;
    }
    Unlock(m_hLock);
RemoteProcessUnstarted:
    return builder.BuildForDummy(ppModules);
}

ECode CPseudoProcess::GetModuleByName(
    /* [in] */ WString name,
    /* [out] */ IModule ** ppModule)
{
    ECode ec = NOERROR;

    if (name.IsNullOrEmpty() || (NULL == ppModule)) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        return E_PROCESS_NOT_ACTIVE;
    }

    Lock(m_hLock);
    if (REMOTE_PROCESS_ALREADY_EXITED()
        || FAILED(_GetProxy())) {
        goto RemoteProcessExited;
    }
    ec = m_pProxy->GetModuleByName(name, ppModule);
    if (REMOTE_PROCESS_JUST_EXITED(ec)) {
        goto RemoteProcessExited;
    }
    Unlock(m_hLock);

    return ec;

RemoteProcessExited:
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
        m_bFinished = TRUE;
    }
    Unlock(m_hLock);
    return E_PROCESS_NOT_ACTIVE;
}

ECode CPseudoProcess::GetModuleByAddress(
    /* [in] */ Address address,
    /* [out] */ IModule ** ppModule)
{
    ECode ec = NOERROR;

    if (NULL == ppModule) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        return E_PROCESS_NOT_ACTIVE;
    }

    Lock(m_hLock);
    if (REMOTE_PROCESS_ALREADY_EXITED()
        || FAILED(_GetProxy())) {
        goto RemoteProcessExited;
    }
    ec = m_pProxy->GetModuleByAddress(address, ppModule);
    if (REMOTE_PROCESS_JUST_EXITED(ec)) {
        goto RemoteProcessExited;
    }
    Unlock(m_hLock);

    return ec;

RemoteProcessExited:
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
        m_bFinished = TRUE;
    }
    Unlock(m_hLock);
    return E_PROCESS_NOT_ACTIVE;
}

ECode CPseudoProcess::GetId(
    /* [out] */ ProcessId * pPid)
{
    if (NULL == pPid) {
        return E_INVALID_ARGUMENT;
    }

    *pPid = m_processId;
    return NOERROR;
}

ECode CPseudoProcess::GetStartInfo(
    /* [out] */ WStringBuf * pName,
    /* [out] */ WStringBuf * pArgs)
{
    WStringBuf * pTmpName = pName;
    WStringBuf * pTmpArgs = pArgs;
    Boolean bNameAlloced = FALSE;
    Boolean bArgsAlloced = FALSE;
    ECode ec = NOERROR;

    if ((pName == NULL || !pName->GetCapacity()) &&
        (pArgs == NULL || !pArgs->GetCapacity())) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        return E_PROCESS_NOT_STARTED;
    }

    if ((NULL == pTmpName) || (!pTmpName->GetCapacity())) {
        pTmpName = WStringBuf::Alloc(AURA_MAX_PATH);
        if (NULL == pTmpName) {
            ec = E_OUT_OF_MEMORY;
            goto Exit;
        }
        bNameAlloced = TRUE;
    }
    if ((NULL == pTmpArgs) || (!pTmpArgs->GetCapacity())) {
        pTmpArgs = WStringBuf::Alloc(AURA_MAX_PATH);
        if (NULL == pTmpArgs) {
            ec = E_OUT_OF_MEMORY;
            goto Exit;
        }
        bArgsAlloced = TRUE;
    }
    ec = CPseudoCentral::Process_GetStartInfo(m_processId, pTmpName, pTmpArgs);

Exit:
    if (bNameAlloced) {
        WStringBuf::Free(pTmpName);
    }
    if (bArgsAlloced) {
        WStringBuf::Free(pTmpArgs);
    }

    return ec;
}

ECode CPseudoProcess::GetExitCode(
    /* [out] */ ExitCode * pExitCode)
{
    ECode ec = NOERROR;

    if (NULL == pExitCode) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        return E_PROCESS_NOT_EXITED;
    }
    ec = CPseudoCentral::Process_GetExitCode(m_processId, pExitCode);
    if (FAILED(ec)) {
        return ec;
    }

    Lock(m_hLock);
    m_bFinished = TRUE;
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
    }
    Unlock(m_hLock);

    return NOERROR;
}

ECode CPseudoProcess::GetStartTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        return E_PROCESS_NOT_STARTED;
    }

    return CPseudoCentral::Process_GetStartTime(m_processId, pTime);
}

ECode CPseudoProcess::GetExitTime(
    /* [out] */ SystemTime * pTime)
{
    ECode ec = NOERROR;

    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        return E_PROCESS_NOT_EXITED;
    }

    ec = CPseudoCentral::Process_GetExitTime(m_processId, pTime);
    if (FAILED(ec)) {
        return ec;
    }

    Lock(m_hLock);
    m_bFinished = TRUE;
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
    }
    Unlock(m_hLock);
    return NOERROR;
}

ECode CPseudoProcess::GetProcessorTimes(
    /* [out] */ Millisecond32 * pUserTime,
    /* [out] */ Millisecond32 * pKernelTime)
{
    ECode ec = NOERROR;

    if ((NULL == pUserTime)
        && (NULL == pKernelTime)) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        if (NULL != pUserTime) {
            *pUserTime = 0;
        }
        if (NULL != pKernelTime) {
            *pKernelTime = 0;
        }
        return NOERROR;
    }

    Lock(m_hLock);
    if (REMOTE_PROCESS_ALREADY_EXITED()
        || FAILED(_GetProxy())) {
        goto RemoteProcessExited;
    }
    ec = m_pProxy->GetProcessorTimes(pUserTime, pKernelTime);
    if (REMOTE_PROCESS_JUST_EXITED(ec)) {
        goto RemoteProcessExited;
    }
    Unlock(m_hLock);

RemoteProcessExited:
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
        m_bFinished = TRUE;
    }
    Unlock(m_hLock);

    return CPseudoCentral::Process_GetProcessorTimes(m_processId,
                                        pUserTime, pKernelTime);
}

ECode CPseudoProcess::GetMemorySizes(
    /* [out] */ Int32 * pPrivateVirtualMemorySize,
    /* [out] */ Int32 * pSharedVirtualMemorySize,
    /* [out] */ Int32 * pPrivatePhysicalMemorySize,
    /* [out] */ Int32 * pSharedPhysicalMemorySize)
{
    ECode ec = NOERROR;

    if ((NULL == pPrivateVirtualMemorySize)
        && (NULL == pSharedVirtualMemorySize)
        && (NULL == pPrivatePhysicalMemorySize)
        && (NULL == pSharedPhysicalMemorySize)) {
        return E_INVALID_ARGUMENT;
    }

    if (REMOTE_PROCESS_UNSTARTED()) {
        goto RemoteProcessUnstarted;
    }

    Lock(m_hLock);
    if (REMOTE_PROCESS_ALREADY_EXITED()
        || FAILED(_GetProxy())) {
        goto RemoteProcessExited;
    }

    ec = m_pProxy->GetMemorySizes(pPrivateVirtualMemorySize,
                                pSharedPhysicalMemorySize,
                                pPrivatePhysicalMemorySize,
                                pSharedPhysicalMemorySize);
    if (REMOTE_PROCESS_JUST_EXITED(ec)) {
        goto RemoteProcessExited;
    }
    Unlock(m_hLock);

    return ec;

RemoteProcessExited:
    if (NULL != m_pProxy) {
        m_pProxy->Release();
        m_pProxy = NULL;
        m_bFinished = TRUE;
    }
    Unlock(m_hLock);
RemoteProcessUnstarted:
    if (NULL != pPrivateVirtualMemorySize) {
        *pPrivateVirtualMemorySize = 0;
    }
    if (NULL != pSharedVirtualMemorySize) {
        *pSharedVirtualMemorySize = 0;
    }
    if (NULL != pPrivatePhysicalMemorySize) {
        *pPrivatePhysicalMemorySize = 0;
    }
    if (NULL != pSharedPhysicalMemorySize) {
        *pSharedPhysicalMemorySize = 0;
    }

    return NOERROR;
}

ECode CPseudoProcessFactory::CreateObject(
    /* [out] */ PseudoObject ** ppPseudoObject)
{
    CPseudoProcess * pProcess = NULL;
    ECode ec = NOERROR;

    assert(NULL != ppPseudoObject);

    pProcess = new CPseudoProcess(m_processId);
    if (NULL == pProcess) {
        return E_OUT_OF_MEMORY;
    }

    ec = pProcess->Initialize();
    if (FAILED(ec)) {
        delete pProcess;
        pProcess = NULL;
        return ec;
    }
    pProcess->AddRef();

    *ppPseudoObject = pProcess;

    return NOERROR;
}

